#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import json
import logging
from typing import Dict, Any, Optional

class JoystickConfig:
    """
    游戏手柄配置管理器，负责加载和保存配置
    """
    
    DEFAULT_CONFIG = {
        # 按钮映射，键为按钮ID，值为动作（"start", "stop", "restart"）
        "button_map": {
            "0": "start",  # 按钮0 - 启动进程
            "1": "stop",   # 按钮1 - 停止进程
            "2": "restart" # 按钮2 - 重启进程
        },
        
        # 按钮对应的进程，键为按钮ID，值为进程名称
        "button_to_process": {
            "0": "default",
            "1": "default",
            "2": "default"
        },
        
        # 进程命令配置，键为进程名称，值为要执行的命令
        "commands": {
            "default": "roslaunch my_package my_launch_file.launch",
            "navigation": "roslaunch navigation_package navigation.launch",
            "camera": "roslaunch camera_package camera.launch"
        },
        
        # 按键消抖时间（秒）
        "debounce_time": 0.5
    }
    
    def __init__(self, config_file: Optional[str] = None) -> None:
        """
        初始化配置管理器
        
        :param config_file: 配置文件路径，如果为None则使用默认配置
        """
        self.logger = logging.getLogger("JoystickConfig")
        self.config_file = config_file
        self.config = self.DEFAULT_CONFIG.copy()
        
        if config_file:
            self.load_config()
    
    def load_config(self) -> bool:
        """
        从文件加载配置
        
        :return: 是否成功加载配置
        """
        if not self.config_file or not os.path.exists(self.config_file):
            self.logger.warning(f"配置文件 '{self.config_file}' 不存在，使用默认配置")
            return False
        
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                loaded_config = json.load(f)
                
                # 安全地合并配置
                self._safe_merge_config(loaded_config)
            
            self.logger.info(f"已从 '{self.config_file}' 加载配置")
            return True
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {str(e)}")
            return False
    
    def _safe_merge_config(self, loaded_config: Dict[str, Any]) -> None:
        """
        安全地合并配置，处理不同类型的配置值
        
        :param loaded_config: 从文件加载的配置
        """
        # 对于嵌套字典的配置项，合并字典
        for key, value in loaded_config.items():
            if key in self.config:
                # 如果原配置和新配置都是字典，进行合并
                if isinstance(self.config[key], dict) and isinstance(value, dict):
                    self.config[key].update(value)
                else:
                    # 对于非字典类型，直接替换
                    self.config[key] = value
            else:
                # 如果是新增的配置项，直接添加
                self.config[key] = value
    
    def save_config(self, config_file: Optional[str] = None) -> bool:
        """
        保存配置到文件
        
        :param config_file: 配置文件路径，如果为None则使用初始化时指定的路径
        :return: 是否成功保存配置
        """
        target_file = config_file or self.config_file
        
        if not target_file:
            self.logger.error("未指定配置文件路径，无法保存配置")
            return False
        
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(os.path.abspath(target_file)), exist_ok=True)
            
            with open(target_file, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, ensure_ascii=False, indent=4)
            
            self.logger.info(f"已保存配置到 '{target_file}'")
            return True
        except Exception as e:
            self.logger.error(f"保存配置文件失败: {str(e)}")
            return False
    
    def get_config(self) -> Dict[str, Any]:
        """
        获取当前配置
        
        :return: 当前配置字典
        """
        return self.config
    
    def update_config(self, new_config: Dict[str, Any]) -> None:
        """
        更新配置
        
        :param new_config: 新的配置字典
        """
        if not isinstance(new_config, dict):
            self.logger.error("更新配置失败: 新配置必须是字典类型")
            return
        
        # 使用安全的配置合并方法
        self._safe_merge_config(new_config)
        self.logger.info("配置已更新")
    
    def set_command(self, process_name: str, command: str) -> None:
        """
        设置进程的启动命令
        
        :param process_name: 进程名称
        :param command: 启动命令
        """
        if "commands" not in self.config:
            self.config["commands"] = {}
        
        self.config["commands"][process_name] = command
        self.logger.info(f"已设置进程 '{process_name}' 的启动命令: {command}")
    
    def set_button_action(self, button_id: int, action: str, process_name: Optional[str] = None) -> None:
        """
        设置按钮动作和关联的进程
        
        :param button_id: 按钮ID
        :param action: 动作名称("start", "stop", "restart")
        :param process_name: 进程名称，如果不指定则使用"default"
        """
        str_button_id = str(button_id)
        
        if action not in ["start", "stop", "restart"]:
            self.logger.error(f"无效的动作: {action}，必须是 'start', 'stop' 或 'restart'")
            return
        
        # 更新按钮映射
        if "button_map" not in self.config:
            self.config["button_map"] = {}
        
        self.config["button_map"][str_button_id] = action
        
        # 更新按钮对应的进程
        if process_name:
            if "button_to_process" not in self.config:
                self.config["button_to_process"] = {}
            
            self.config["button_to_process"][str_button_id] = process_name
        
        self.logger.info(f"已设置按钮 {button_id} 的动作: {action}, 进程: {process_name or 'default'}")
    
    def set_debounce_time(self, time_seconds: float) -> None:
        """
        设置按键消抖时间
        
        :param time_seconds: 消抖时间（秒）
        """
        if time_seconds < 0:
            self.logger.error(f"无效的消抖时间: {time_seconds}，必须大于等于0")
            return
        
        self.config["debounce_time"] = time_seconds
        self.logger.info(f"已设置按键消抖时间: {time_seconds} 秒") 